package cn.melonlib.cores.commons.comp.utils.impl;

import cn.melonlib.cores.commons.comp.utils.EntityWrapperContainer;
import cn.melonlib.cores.commons.comp.utils.IEntityWrapper;
import cn.melonlib.cores.commons.model.entity.IEntity;
import cn.melonlib.cores.commons.model.entity.StringIdEntity;
import cn.hutool.core.bean.BeanUtil;
import cn.melonlib.cores.jpa.comp.Dao;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

@Component
public class FormEntityWrapper extends ApplicationObjectSupport implements IEntityWrapper, ApplicationRunner {

    @Resource
    private ObjectMapper jsonMapper;

    @Resource
    private EntityWrapperContainer entityWrapperContainer;

    @Resource
    private Dao dao;

    public <ENTITY extends IEntity<? extends Serializable>> ENTITY wrap(String prefix, Class<ENTITY> entityClass) throws Exception {
        HttpServletRequest request=((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        Set<Field> fields=new HashSet<>();
        Class tmpclass=entityClass;
        if(StringUtils.isNotBlank(prefix)){
            prefix+=".";
        }else{
            prefix="";
        }
        while(IEntity.class.isAssignableFrom(tmpclass)){
            if(StringIdEntity.class.getName().equals(entityClass.getName())){
                break;
            }
            fields.addAll(Arrays.asList(tmpclass.getDeclaredFields()));
            tmpclass=tmpclass.getSuperclass();
        }
        IEntity entity=null;
        String id=request.getParameter(prefix+"id");
        if(StringUtils.isNotBlank(id)){
            entity=this.dao.getEntity(entityClass, id);
        }else{
            entity= entityClass.getDeclaredConstructor().newInstance();
        }

        for (Field field : fields) {
            Type decls=field.getGenericType();
            if((decls instanceof Class) && IEntity.class.isAssignableFrom((Class<?>) decls)){
                try{
                    String val=request.getParameter(prefix+field.getName()+".id");
                    if(val==null){
                        continue;
                    }
                    if(StringUtils.isNotBlank(val)){
                        BeanUtil.setProperty(entity,field.getName(), this.dao.getEntity((Class)decls, val));
                    }else{
                        BeanUtil.setProperty(entity,field.getName(),null);
                    }
                }catch(Exception e){
                    logger.error(e.getMessage(),e);
                }
            } else if(decls instanceof Class){
                Class<?> fcls=(Class<?>) decls;

                String val=request.getParameter(prefix+field.getName());
                if(val==null){
                    continue;
                }
                if(StringUtils.isBlank(val)){
                    BeanUtil.setProperty(entity, field.getName(), null);
                    continue;
                }
                if(CharSequence.class.isAssignableFrom(fcls)){
                    BeanUtil.setProperty(entity,field.getName(), val);
                }else if(fcls.isEnum()){
                    if(NumberUtils.isDigits(val.toString())){
                        BeanUtil.setProperty(entity, field.getName(), Array.get(fcls.getMethod("values").invoke(null), NumberUtils.createInteger(val.toString())));
                    }else{
                        BeanUtil.setProperty(entity, field.getName(),fcls.getMethod("valueOf",String.class).invoke(null,val));
                    }
                }else{
                    if(Double.class.isAssignableFrom((Class<?>) decls)){
                        BeanUtil.setProperty(entity, field.getName(),  NumberUtils.createDouble(val));
                    }else if(Long.class.isAssignableFrom((Class<?>) decls)){
                        BeanUtil.setProperty(entity,field.getName(),NumberUtils.createLong(ObjectUtils.defaultIfNull(val,"0")));
                    }else if(Integer.class.isAssignableFrom((Class<?>) decls)){
                        BeanUtil.setProperty(entity, field.getName(),  NumberUtils.createInteger(val));
                    }else if(((Class<?>) decls).isEnum()){
                        BeanUtil.setProperty(entity,fcls.getName(), fcls.getMethod("valueOf",String.class).invoke(null, val));
                    }else if(Boolean.class.isAssignableFrom((Class<?>)decls)){
                        BeanUtil.setProperty(entity, field.getName(),  BooleanUtils.toBoolean(val));
                    }else{
                        BeanUtil.setProperty(entity, field.getName(),  val);
                    }
                }
            }
        }
        return (ENTITY) entity ;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        this.entityWrapperContainer.addWrap(EntityWrapperContainer.FORM,this);
    }
}
